﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Messaging;
using Framework.Event;

namespace Framework.Bus
{
    public class MSMQEventBus : EventBus
    {
        #region MSMQ
        private readonly bool useInternalTransaction;
        private readonly MSMQBusOptions options;
        private readonly MessageQueue MSMessageQueue;
        #endregion

        
        public MSMQEventBus(IMessageDispatcher dispatcher) : base(dispatcher)
        {

        }

        public MSMQEventBus(IMessageDispatcher dispatcher, string path, bool useInternalTransaction = true)
            : this(dispatcher)
        {
            if (!string.IsNullOrEmpty(path))
            {
                this.options = new MSMQBusOptions(path);
                try
                {
                    this.MSMessageQueue = new MessageQueue(path,
                     options.SharedModeDenyReceive,
                     options.EnableCache, options.QueueAccessMode);
                    MSMessageQueue.Formatter = new BinaryMessageFormatter(System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full,
                    System.Runtime.Serialization.Formatters.FormatterTypeStyle.TypesAlways);
                    this.useInternalTransaction = useInternalTransaction;
                }
                catch (System.Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }

        #region IBus Members
        public override void Commit()
        {
            lock (queueLock)
            {
                if (MSMessageQueue != null)
                {
                    if (this.useInternalTransaction)
                    {
                        using (MessageQueueTransaction transaction = new MessageQueueTransaction())
                        {
                            try
                            {
                                transaction.Begin();
                                while (messageQueue.Count > 0)
                                {
                                    var eventMetaData = messageQueue.Dequeue();
                                    IEvent msg = eventMetaData.Event;
                                    if (eventMetaData.BeforeCommitAction != null)
                                    {
                                        eventMetaData.BeforeCommitAction(msg);
                                    }
                                    SendMSMQMessage(msg, transaction);
                                }
                                transaction.Commit();
                            }
                            catch
                            {
                                transaction.Abort();
                                throw;
                            }
                        }
                    }
                    else
                    {
                        while (messageQueue.Count > 0)
                        {
                            var eventMetaData = messageQueue.Dequeue();
                            IEvent msg = eventMetaData.Event;
                            if (eventMetaData.BeforeCommitAction != null)
                            {
                                eventMetaData.BeforeCommitAction(msg);
                            }
                            SendMSMQMessage(msg);
                        }
                    }
                }

            }
            committed = true;
        }

        protected virtual void SendMSMQMessage(IEvent message, MessageQueueTransaction transaction = null)
        {
            Message msmqMessage = new Message(message);
            msmqMessage.Recoverable = true;
            msmqMessage.Formatter = MSMessageQueue.Formatter;
            if (useInternalTransaction)
            {
                if (transaction == null)
                    throw new ArgumentNullException("transaction");
                MSMessageQueue.Send(msmqMessage, transaction);
            }
            else
            {
                MSMessageQueue.Send(msmqMessage, MessageQueueTransactionType.Automatic);
            }
        }


        #endregion
    }
}
